using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Runtime.InteropServices;
using ViewOnCore.Helper.WPF;
using ViewOnCore.HostContract.Media;
using ViewOnCore.Device;
using ViewOnCore.HostContract;

namespace UI.Control
{
    /// <summary>
    /// Display information about a track
    /// </summary>
    public partial class MusicInfoControl : System.Windows.Controls.UserControl
    {

        #region Variables

        private List<Music> _selectedMusics;
        private string _previousTextValue = null;
        private bool _isReadOnly = true;

        private ToggleButton[] _tbtRatings;
        private bool _isUpdating = false;

        private MusicIntellisenseManager _intellisenseManager = new MusicIntellisenseManager();

        #endregion

        #region Constructor

        public MusicInfoControl()
        {
            InitializeComponent();

            //---- Media kernel events
            Host.MediaService.MediaDataOperation += new MediaDataOperationEventHandler(MediaKernel_MediasOperation);

            imgFrontCover.Drop += new DragEventHandler(imgFrontCover_Drop);
            imgFrontCover.DragOver += new DragEventHandler(imgFrontCover_DragOver);
            imgFrontCoverBorder.Drop += new DragEventHandler(imgFrontCover_Drop);
            imgFrontCoverBorder.DragOver += new DragEventHandler(imgFrontCover_DragOver);
            imgFrontCover.MouseLeftButtonUp += new MouseButtonEventHandler(imgFrontCover_MouseLeftButtonUp);
            imgFrontCoverHighlight.MouseLeftButtonUp += new MouseButtonEventHandler(imgFrontCover_MouseLeftButtonUp);

            txtTitle.GotFocus += new RoutedEventHandler(control_GotFocus);
            txtArtist.GotFocus += new RoutedEventHandler(control_GotFocus);
            txtAlbum.GotFocus += new RoutedEventHandler(control_GotFocus);
            txtComposer.GotFocus += new RoutedEventHandler(control_GotFocus);
            txtTrack.GotFocus += new RoutedEventHandler(control_GotFocus);
            txtTrackCount.GotFocus += new RoutedEventHandler(control_GotFocus);
            txtYear.GotFocus += new RoutedEventHandler(control_GotFocus);
            txtComment.GotFocus += new RoutedEventHandler(control_GotFocus);
            txtGenre.GotFocus += new RoutedEventHandler(control_GotFocus);

            txtTitle.LostFocus += new RoutedEventHandler(control_LostFocus);
            txtArtist.LostFocus += new RoutedEventHandler(control_LostFocus);
            txtAlbum.LostFocus += new RoutedEventHandler(control_LostFocus);
            txtComposer.LostFocus += new RoutedEventHandler(control_LostFocus);
            txtTrack.LostFocus += new RoutedEventHandler(control_LostFocus);
            txtTrackCount.LostFocus += new RoutedEventHandler(control_LostFocus);
            txtYear.LostFocus += new RoutedEventHandler(control_LostFocus);
            txtComment.LostFocus += new RoutedEventHandler(control_LostFocus);
            txtGenre.LostFocus += new RoutedEventHandler(control_LostFocus);

            tbtRating1.Unchecked += new RoutedEventHandler(tbtRating_Unchecked);
            tbtRating1.Checked += new RoutedEventHandler(tbtRating_Checked);
            tbtRating2.Unchecked += new RoutedEventHandler(tbtRating_Unchecked);
            tbtRating2.Checked += new RoutedEventHandler(tbtRating_Checked);
            tbtRating3.Unchecked += new RoutedEventHandler(tbtRating_Unchecked);
            tbtRating3.Checked += new RoutedEventHandler(tbtRating_Checked);
            tbtRating4.Unchecked += new RoutedEventHandler(tbtRating_Unchecked);
            tbtRating4.Checked += new RoutedEventHandler(tbtRating_Checked);
            tbtRating5.Unchecked += new RoutedEventHandler(tbtRating_Unchecked);
            tbtRating5.Checked += new RoutedEventHandler(tbtRating_Checked);

            txtPath.MouseUp += new MouseButtonEventHandler(txtPath_MouseUp);

            _tbtRatings = new ToggleButton[5] { tbtRating1, tbtRating2, tbtRating3, tbtRating4, tbtRating5 };

            //---- Intellisense
            txtArtist.InitializeIntellisense(_intellisenseManager, 0);
            txtGenre.InitializeIntellisense(_intellisenseManager, 1);
            txtComposer.InitializeIntellisense(_intellisenseManager, 2);
            txtAlbum.InitializeIntellisense(_intellisenseManager, 3);
        }

        #endregion

        #region Refresh

        public void Refresh(List<Music> selectedMusics)
        {
            _selectedMusics = selectedMusics;
            Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                            (DelegateInvoker)delegate { AsyncRefresh(); });
        }

        public void AsyncRefresh()
        {
            _isUpdating = true;

            UpdateIntellisenseDB();

            try
            {
                //---- Reset
                _isReadOnly = Host.MediaService.CurrentMediaDeviceFeature.IsTagUpdateSupported;
                foreach (Music music in _selectedMusics)
                {
                    if (!_isReadOnly)
                        break;
                    _isReadOnly = _isReadOnly & Host.MediaService.CurrentMediaDeviceFeature.CanUpdateTag(music);
                }

                _isReadOnly = !_isReadOnly;

                txtTitle.IsReadOnly = _isReadOnly;
                txtArtist.IsReadOnly = _isReadOnly;
                txtAlbum.IsReadOnly = _isReadOnly;
                txtComposer.IsReadOnly = _isReadOnly;
                txtTrack.IsReadOnly = _isReadOnly;
                txtTrackCount.IsReadOnly = _isReadOnly;
                txtYear.IsReadOnly = _isReadOnly;
                txtDuration.IsReadOnly = true;
                txtComment.IsReadOnly = _isReadOnly;
                txtGenre.IsReadOnly = _isReadOnly;
                tbtRating1.IsEnabled = !_isReadOnly;
                tbtRating2.IsEnabled = !_isReadOnly;
                tbtRating3.IsEnabled = !_isReadOnly;
                tbtRating4.IsEnabled = !_isReadOnly;
                tbtRating5.IsEnabled = !_isReadOnly;

                imgFrontCoverHighlight.IsEnabled = !_isReadOnly;

                txtTitle.Text = "";
                txtArtist.Text = "";
                txtAlbum.Text = "";
                txtComposer.Text = "";
                txtTrack.Text = "";
                txtTrackCount.Text = "";
                txtYear.Text = "";
                txtDuration.Text = "";
                txtComment.Text = "";
                txtGenre.Text = "";
                txtPath.Text = "";
                tbtRating1.IsChecked = false;
                tbtRating2.IsChecked = false;
                tbtRating3.IsChecked = false;
                tbtRating4.IsChecked = false;
                tbtRating5.IsChecked = false;
                imgFrontCover.Source = null;

                if (_selectedMusics.Count < 1)
                    return;

                if (IsSameFieldValue(MediaDataTagField.Title))
                    txtTitle.Text = _selectedMusics[0].Title;
                if (IsSameFieldValue(MediaDataTagField.Artist))
                    txtArtist.Text = _selectedMusics[0].Artist;
                if (IsSameFieldValue(MediaDataTagField.Album))
                    txtAlbum.Text = _selectedMusics[0].Album;
                if (IsSameFieldValue(MediaDataTagField.Composer))
                    txtComposer.Text = _selectedMusics[0].Composer;
                if (_selectedMusics.Count == 1)
                    txtTrack.Text = _selectedMusics[0].Track.ToString();
                if (IsSameFieldValue(MediaDataTagField.TrackCount))
                    txtTrackCount.Text = _selectedMusics[0].TrackCount.ToString();
                if (IsSameFieldValue(MediaDataTagField.Year))
                    txtYear.Text = _selectedMusics[0].Year.ToString();
                if (Host.MediaService.CurrentMediaDeviceFeature.IsManageFilePathSupported)
                {
                    lblPath.Visibility = Visibility.Visible;
                    txtPath.Visibility = Visibility.Visible;

                    if (IsSameFieldValue(MediaDataTagField.Path))
                        txtPath.Text = _selectedMusics[0].Path;
                }
                else
                {
                    lblPath.Visibility = Visibility.Hidden;
                    txtPath.Visibility = Visibility.Hidden;
                }

                txtDuration.Text = _selectedMusics[0].FormattedDuration;

                if (IsSameFieldValue(MediaDataTagField.Genre))
                    txtComment.Text = (string)_selectedMusics[0].Comment;
                if (IsSameFieldValue(MediaDataTagField.Genre))
                    txtGenre.Text = (string)_selectedMusics[0].Genre;

                if (IsSameFieldValue(MediaDataTagField.Rating))
                {
                    tbtRating1.IsChecked = _selectedMusics[0].IsRating1;
                    tbtRating2.IsChecked = _selectedMusics[0].IsRating2;
                    tbtRating3.IsChecked = _selectedMusics[0].IsRating3;
                    tbtRating4.IsChecked = _selectedMusics[0].IsRating4;
                    tbtRating5.IsChecked = _selectedMusics[0].IsRating5;
                }

                if (IsSameFieldValue(MediaDataTagField.Image))
                {
                    System.Drawing.Bitmap bitmap = ((System.Drawing.Bitmap)_selectedMusics[0].Image);
                    if (bitmap == null)
                    {
                        imgFrontCover.Source = null;
                        return;
                    }
                    imgFrontCover.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(),
                                                                                IntPtr.Zero,
                                                                                Int32Rect.Empty,
                                                                                System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
                }
                else imgFrontCover.Source = null;
            }
            finally
            {
                _isUpdating = false;
            }
        }

        #endregion

        #region Media Kernel Events

        void MediaKernel_MediasOperation(MediaFeature feature, MediaItem featureItem, MediaOperationKind operationKind, object parameter)
        {
            if (operationKind == MediaOperationKind.Removed)
                Refresh(new List<Music>());
        }

        #endregion

        #region IsSameFieldValue

        private bool IsSameFieldValue(MediaDataTagField field)
        {
            if (field == MediaDataTagField.Path)
            {
                if (_selectedMusics.Count == 1)
                    return true;

                return false;
            }

            Music previousMusic = _selectedMusics[0];
            for (int index = 1; index < _selectedMusics.Count; index++)
            {
                Music currentMusic = _selectedMusics[index];
                if (field == MediaDataTagField.Title && currentMusic.Title != previousMusic.Title)
                    return false;
                if (field == MediaDataTagField.Artist && currentMusic.Artist != previousMusic.Artist)
                    return false;
                if (field == MediaDataTagField.Album && currentMusic.Album != previousMusic.Album)
                    return false;
                if (field == MediaDataTagField.Composer && currentMusic.Composer != previousMusic.Composer)
                    return false;
                if (field == MediaDataTagField.TrackCount && currentMusic.TrackCount != previousMusic.TrackCount)
                    return false;
                if (field == MediaDataTagField.Year && currentMusic.Year != previousMusic.Year)
                    return false;
                if (field == MediaDataTagField.Comment && currentMusic.Comment != previousMusic.Comment)
                    return false;
                if (field == MediaDataTagField.Genre && currentMusic.Genre != previousMusic.Genre)
                    return false;
                if (field == MediaDataTagField.Rating && currentMusic.Rating != previousMusic.Rating)
                    return false;
                if (field == MediaDataTagField.FrontCoverPicture && !currentMusic.Image.Equals(previousMusic.Image))
                    return false;
            }

            return true;
        }

        #endregion

        #region GotFocus

        void control_GotFocus(object sender, RoutedEventArgs e)
        {
            TextBox textBox = sender as TextBox;
            IntellisenseTextBox intellisenseTextBox = sender as IntellisenseTextBox;

            if (textBox != null)
            {
                textBox.SelectAll();
                _previousTextValue = textBox.Text;
            }
            else if (intellisenseTextBox != null)
            {
                intellisenseTextBox.SelectAll();
                _previousTextValue = intellisenseTextBox.Text;
            }
        }

        #endregion

        #region LostFocus

        void control_LostFocus(object sender, RoutedEventArgs e)
        {
            if (_isUpdating)
                return;

            if (_selectedMusics == null)
                return;

            TextBox textBox = sender as TextBox;
            IntellisenseTextBox intellisenseTextBox = sender as IntellisenseTextBox;

            string newTextValue = "";

            if (textBox != null)
                newTextValue = textBox.Text.Trim();
            else if (intellisenseTextBox != null)
                newTextValue = intellisenseTextBox.Text.Trim();

            if (_previousTextValue == null)
                return;
            if (_previousTextValue.Equals(newTextValue))
                return;

            _isUpdating = true;

            MediaDataTagField field = MediaDataTagField.Title;
            object newValue = null;

            if (sender == txtAlbum)
            {
                field = MediaDataTagField.Album;
                newValue = txtAlbum.Text.Trim();
            }
            else if (sender == txtArtist)
            {
                field = MediaDataTagField.Artist;
                newValue = txtArtist.Text.Trim();
            }
            else if (sender == txtComment)
            {
                field = MediaDataTagField.Comment;
                newValue = txtComment.Text.Trim();
            }
            else if (sender == txtComposer)
            {
                field = MediaDataTagField.Composer;
                newValue = txtComposer.Text.Trim();
            }
            else if (sender == txtGenre)
            {
                field = MediaDataTagField.Genre;
                newValue = txtGenre.Text.Trim();
            }
            else if (sender == txtTitle)
            {
                field = MediaDataTagField.Title;
                newValue = txtTitle.Text.Trim();
            }
            else if (sender == txtTrack)
            {
                field = MediaDataTagField.Track;
                try
                {
                    newValue = Int32.Parse(txtTrack.Text.Trim());
                }
                catch { /* PUT IN RED */ }
            }
            else if (sender == txtTrackCount)
            {
                field = MediaDataTagField.TrackCount;
                try
                {
                    newValue = Int32.Parse(txtTrackCount.Text.Trim());
                }
                catch { /* PUT IN RED */ }
            }
            else if (sender == txtYear)
            {
                field = MediaDataTagField.Year;
                try
                {
                    newValue = Int32.Parse(txtYear.Text.Trim());
                }
                catch { /* PUT IN RED */ }
            }

            //---- Update intellisense
            UpdateIntellisenseDB();

            //---- Update
            List<MediaData> mediasToUpdate = new List<MediaData>(_selectedMusics.Count);
            foreach (MediaData media in _selectedMusics)
                mediasToUpdate.Add(media);
            Host.MediaService.UpdateMediaDatas(mediasToUpdate, Host.MediaService.CurrentMediaDeviceFeature, field, newValue);

            _isUpdating = false;
        }

        #endregion

        #region tbtRating_Checked

        void tbtRating_Checked(object sender, RoutedEventArgs e)
        {
            if (_isUpdating)
                return;

            if (_selectedMusics == null)
                return;

            _isUpdating = true;

            bool checkIt = false;
            for (int index = 4; index > -1; index--)
            {
                if (sender == _tbtRatings[index])
                    checkIt = true;
                _tbtRatings[index].IsChecked = checkIt;
            }

            int rating;
            if (tbtRating5.IsChecked == true)
                rating = 5;
            else if (tbtRating4.IsChecked == true)
                rating = 4;
            else if (tbtRating3.IsChecked == true)
                rating = 3;
            else if (tbtRating2.IsChecked == true)
                rating = 2;
            else if (tbtRating1.IsChecked == true)
                rating = 1;
            else
                rating = 0;

            //---- Update
            List<MediaData> mediasToUpdate = new List<MediaData>(_selectedMusics.Count);
            foreach (MediaData media in _selectedMusics)
                mediasToUpdate.Add(media);
            Host.MediaService.UpdateMediaDatas(mediasToUpdate, Host.MediaService.CurrentMediaDeviceFeature, MediaDataTagField.Rating, rating);

            _isUpdating = false;
        }

        void tbtRating_Unchecked(object sender, RoutedEventArgs e)
        {
            if (_isUpdating)
                return;

            if (_selectedMusics == null)
                return;

            _isUpdating = true;

            bool checkIt = false;
            for (int index = 4; index > -1; index--)
            {
                if (sender == _tbtRatings[index])
                    checkIt = true;

                if (index == 0 && sender == _tbtRatings[0] && _tbtRatings[0].IsChecked == false)
                    _tbtRatings[0].IsChecked = false; // Inverse the value
                else
                    _tbtRatings[index].IsChecked = checkIt;
            }

            int rating;
            if (tbtRating5.IsChecked == true)
                rating = 5;
            else if (tbtRating4.IsChecked == true)
                rating = 4;
            else if (tbtRating3.IsChecked == true)
                rating = 3;
            else if (tbtRating2.IsChecked == true)
                rating = 2;
            else if (tbtRating1.IsChecked == true)
                rating = 1;
            else
                rating = 0;

            //---- Update
            List<MediaData> mediasToUpdate = new List<MediaData>(_selectedMusics.Count);
            foreach (MediaData media in _selectedMusics)
                mediasToUpdate.Add(media);
            Host.MediaService.UpdateMediaDatas(mediasToUpdate, Host.MediaService.CurrentMediaDeviceFeature, MediaDataTagField.Rating, rating);

            _isUpdating = false;
        }

        #endregion

        #region Click on file path

        void txtPath_MouseUp(object sender, MouseButtonEventArgs e)
        {
            System.Diagnostics.Process proc = new System.Diagnostics.Process();
            proc.EnableRaisingEvents = false;
            proc.StartInfo.FileName = "explorer";

            Music music = _selectedMusics[0];

            string directory = new System.IO.FileInfo(music.Path).Directory.FullName;

            proc.StartInfo.Arguments = "\"" + directory + "\\\"";
            proc.Start();
        }

        #endregion

        #region Image drag & drop

        void imgFrontCover_DragOver(object sender, DragEventArgs e)
        {
            if (_isReadOnly)
            {
                e.Effects = DragDropEffects.None;
                e.Handled = true;
                return;
            }

            //---- Get data
            string[] imageFilePaths = (string[])e.Data.GetData(DataFormats.FileDrop);
            if (imageFilePaths == null)
            {
                e.Effects = DragDropEffects.None;
                e.Handled = true;
                return;
            }

            foreach (string filePath in imageFilePaths)
            {
                FileInfo fileInfo = new FileInfo(filePath);
                string ext = fileInfo.Extension.ToLower();

                if (ext != ".gif" && ext != ".png" && ext != ".jpg" && ext != ".jpeg" && ext != ".bmp")
                {
                    e.Effects = DragDropEffects.None;
                    e.Handled = true;
                    return;
                }
            }

            //---- Ok, can handle it
            e.Effects = DragDropEffects.Copy;
            e.Handled = true;
        }

        void imgFrontCover_Drop(object sender, DragEventArgs e)
        {
            if (_isReadOnly)
            {
                e.Effects = DragDropEffects.None;
                e.Handled = true;
                return;
            }

            //---- Get data
            string[] imageFilePaths = (string[])e.Data.GetData(DataFormats.FileDrop);
            if (imageFilePaths == null)
            {
                e.Effects = DragDropEffects.None;
                e.Handled = true;
                return;
            }
            foreach (string filePath in imageFilePaths)
            {
                FileInfo fileInfo = new FileInfo(filePath);
                string ext = fileInfo.Extension.ToLower();

                if (ext != ".gif" && ext != ".png" && ext != ".jpg" && ext != ".jpeg" && ext != ".bmp")
                {
                    e.Effects = DragDropEffects.None;
                    e.Handled = true;
                    return;
                }
            }

            //---- Put the image in the music file
            UpdateTagImage(imageFilePaths[0]);

            //---- Set the right effect
            e.Effects = DragDropEffects.Copy;
            e.Handled = true;
        }

        #endregion

        #region Image Dialog

        void imgFrontCover_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (_isReadOnly)
                return;

            if (e.ClickCount > 1)
                return;
            FileBrowserDialog dialog = new FileBrowserDialog();
            dialog.Filter = "Images files|*.gif;*.jpg;*.jpeg;*.png;*.bmp|All files (*.*)|*.*";
            //dialog.InitialDirectory = initialDirectory;
            dialog.Title = "Select an image file";
            dialog.Multiselect = false;
            dialog.CheckFileExists = true;

            if (dialog.ShowDialog() != FileBrowserDialog.DialogResult.OK)
                return;

            UpdateTagImage(dialog.FileName);
        }

        #endregion

        #region Image update

        private void UpdateTagImage(string path)
        {
            //---- Put the image in the music file
            System.Drawing.Bitmap bitmap = System.Drawing.Bitmap.FromFile(path) as System.Drawing.Bitmap;

            //---- Update the image
            if (bitmap == null)
            {
                imgFrontCover.Source = null;
                return;
            }
            imgFrontCover.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(),
                                                                        IntPtr.Zero,
                                                                        Int32Rect.Empty,
                                                                        System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());

            //---- Set the image tags
            // convert...
            List<MediaData> mediasToUpdate = new List<MediaData>(_selectedMusics.Count);
            foreach (MediaData mediaData in _selectedMusics)
                mediasToUpdate.Add(mediaData);

            Host.MediaService.UpdateMediaDatas(mediasToUpdate, Host.MediaService.CurrentMediaDeviceFeature, MediaDataTagField.Image, bitmap);
        }

        #endregion

        #region UpdateIntellisenseDB

        public void UpdateIntellisenseDB()
        {
            _intellisenseManager.UpdateDB(Host.MediaService.CurrentMediaDeviceFeature.MediaDatas);
        }

        #endregion

    }

    #region MusicIntellisenseManager

    sealed public class MusicIntellisenseManager : ViewOnCore.Helper.WPF.IntellisenseManager
    {

        #region Constructor

        public MusicIntellisenseManager()
            : base(4)
        {
        }

        #endregion

        #region Visit

        override public void Visit(MediaData mediaData)
        {
            Music music = mediaData as Music;
            if (music == null)
                return;

            lock (UpdatingDatabases)
            {
                // Artist
                if (!UpdatingDatabases[0].ContainsKey(music.Artist))
                    UpdatingDatabases[0].Add(music.Artist, music.Artist);

                // Genre
                if (!UpdatingDatabases[1].ContainsKey(music.Genre))
                    UpdatingDatabases[1].Add(music.Genre, music.Genre);

                // Composer
                if (!UpdatingDatabases[2].ContainsKey(music.Composer))
                    UpdatingDatabases[2].Add(music.Composer, music.Composer);

                // Album
                if (!UpdatingDatabases[3].ContainsKey(music.Album))
                    UpdatingDatabases[3].Add(music.Album, music.Album);
            }
        }

        #endregion

    }

    #endregion

}